#include "stdafx.h"
#include "GeneralCamera.h"

#include <OIS.h>

#include <HSModel/SharedConstants.h>
#include <limits>

GeneralCamera::GeneralCamera(Ogre::Camera* i_cam):
  mp_cam(0),
  m_top_speed(150),
  m_velocity(Ogre::Vector3::ZERO),
  m_going_forward(false),
  m_going_back(false),
  m_going_right(false),
  m_going_left(false),
  m_rotating_left(false),
  m_rotating_right(false),
  m_rotating_up(false),
  m_rotating_down(false),
  m_x_lowerboarder(GroundConstants::ORIGIN_X),
  m_x_upperboarder(GroundConstants::ORIGIN_X + GroundConstants::CELL_SIZE * GroundConstants::CELL_COUNT),
  m_y_lowerboarder(GroundConstants::MIN_HEIGHT),
  m_y_upperboarder(GroundConstants::MAX_HEIGHT + GroundConstants::SKY_HEIGHT),
  m_z_lowerboarder(GroundConstants::ORIGIN_Z),
  m_z_upperboarder(GroundConstants::ORIGIN_Z + GroundConstants::CELL_SIZE * GroundConstants::CELL_COUNT),
  m_zoom_direction(ZD_NONE)
  {
  mp_cam=i_cam;
  };

void GeneralCamera::SetBoarders(Ogre::Real i_xlower, Ogre::Real i_xupper, Ogre::Real i_ylower, Ogre::Real i_yupper, Ogre::Real i_zlower, Ogre::Real i_zupper)
  {
  m_x_lowerboarder=i_xlower;
  m_x_upperboarder=i_xupper;
  m_y_lowerboarder=i_ylower;
  m_y_upperboarder=i_yupper;
  m_z_lowerboarder=i_zlower;
  m_z_upperboarder=i_zupper;
  };

Ogre::Ray GeneralCamera::GetDirectionRay(Ogre::Real i_screenx, Ogre::Real i_screeny)
  {
  return mp_cam->getCameraToViewportRay(i_screenx, i_screeny);
  };

bool GeneralCamera::FrameRenderingQueued (const Ogre::FrameEvent& i_evt)
  {
  Ogre::Vector3 accel = Ogre::Vector3::ZERO;
  if (m_going_forward) 
    accel += mp_cam->getDirection();
  if (m_going_back) 
    accel -= mp_cam->getDirection();
  if (m_going_right) 
    accel += mp_cam->getRight();
  if (m_going_left) 
    accel -= mp_cam->getRight();
  if (m_rotating_left)
    mp_cam->yaw(Ogre::Degree(i_evt.timeSinceLastFrame*m_top_speed/2));
  if (m_rotating_right)
    mp_cam->yaw(Ogre::Degree(-i_evt.timeSinceLastFrame*m_top_speed/2));
  if (m_rotating_up)
    mp_cam->pitch(Ogre::Degree(i_evt.timeSinceLastFrame*m_top_speed/2));
  if (m_rotating_down)
    mp_cam->pitch(Ogre::Degree(-i_evt.timeSinceLastFrame*m_top_speed/2));

  // zooming
  accel += mp_cam->getDirection() * m_zoom_direction;

  // if accelerating, try to reach top speed in a certain time
  if (accel.squaredLength() != 0)
    {
    accel.normalise();
    m_velocity += accel * m_top_speed * i_evt.timeSinceLastFrame * 10;
    }
  // if not accelerating, try to stop in a certain time
  else 
    m_velocity -= m_velocity * i_evt.timeSinceLastFrame * 10;
  Ogre::Real too_small = std::numeric_limits<Ogre::Real>::epsilon();
  // keep camera velocity below top speed and above epsilon
  if (m_velocity.squaredLength() > m_top_speed * m_top_speed)
    {
    m_velocity.normalise();
    m_velocity *= m_top_speed;
    }
  else if (m_velocity.squaredLength() < too_small * too_small)
    m_velocity = Ogre::Vector3::ZERO;

  const Ogre::Real MINIMAL_DISTANCE = 10.0;
  Ogre::Real min_camera_height = GroundConstants::MAX_HEIGHT * GroundConstants::CELL_SIZE + MINIMAL_DISTANCE;
  Ogre::Vector3 move_vec = m_velocity * i_evt.timeSinceLastFrame;
  if(m_zoom_direction != ZD_NONE)
    {
    if(mp_cam->getPosition().y + move_vec.y < min_camera_height)
      move_vec.y = 0;

    if(mp_cam->getPosition().y + move_vec.y < m_y_lowerboarder || mp_cam->getPosition().y + move_vec.y > m_y_upperboarder)
      move_vec.y = 0;
    }
  else 
    move_vec.y = 0;

  if(mp_cam->getPosition().x + move_vec.x < m_x_lowerboarder || mp_cam->getPosition().x + move_vec.x > m_x_upperboarder)
    move_vec.x = 0;
  if(mp_cam->getPosition().z + move_vec.z < m_z_lowerboarder || mp_cam->getPosition().z + move_vec.z > m_z_upperboarder)
    move_vec.z = 0;
  if (m_velocity != Ogre::Vector3::ZERO)
    mp_cam->move(move_vec);
  return true;
  };

void GeneralCamera::InjectKeyDown(const OIS::KeyEvent& evt)
  {
  if (evt.key == OIS::KC_W) 
    m_going_forward = true;
  else if (evt.key == OIS::KC_S)
    m_going_back = true;
  else if (evt.key == OIS::KC_A) 
    m_going_left = true;
  else if (evt.key == OIS::KC_D) 
    m_going_right = true;
  if (evt.key == OIS::KC_LEFT) 
    m_rotating_left = true;
  if (evt.key == OIS::KC_RIGHT) 
    m_rotating_right = true;
  if (evt.key == OIS::KC_UP) 
    m_rotating_up = true;
  if (evt.key == OIS::KC_DOWN) 
    m_rotating_down = true;

  if(evt.key == OIS::KC_PGDOWN)
    m_zoom_direction = ZD_IN;
  else if(evt.key == OIS::KC_PGUP)
    m_zoom_direction = ZD_OUT;
  };

void GeneralCamera::InjectKeyUp(const OIS::KeyEvent& evt)
  {
  if (evt.key == OIS::KC_W) 
    m_going_forward = false;
  else if (evt.key == OIS::KC_S) 
    m_going_back = false;
  else if (evt.key == OIS::KC_A) 
    m_going_left = false;
  else if (evt.key == OIS::KC_D) 
    m_going_right = false;
  if (evt.key == OIS::KC_LEFT) 
    m_rotating_left = false;
  if (evt.key == OIS::KC_RIGHT) 
    m_rotating_right = false;
  if (evt.key == OIS::KC_UP) 
    m_rotating_up = false;
  if (evt.key == OIS::KC_DOWN) 
    m_rotating_down = false;

  if(evt.key == OIS::KC_PGDOWN)
    m_zoom_direction = ZD_NONE;
  else if(evt.key == OIS::KC_PGUP)
    m_zoom_direction = ZD_NONE;
  };
